--------------------------------------------------------------------------
--
--
--   MODULO_PWM
--
--      Este codigo implementa um PWM simples em um CPLD ou FPGA. O
--  conceito é simples: Uma contagem de N bits é comparada com um valor
--  de entrada, também de N bits. O sinal PWM de saida é "setado" no início
--  da contagem, se o valor de comparação é maior que 0, e resetado quando
--  a contagem atinge um valor igual ao de comparação. A contagem é zerada
--  ao chegar a 255 (então, de fato a contagem vai de 0 a 254), e assim se
--  conseguem razões ciclicas entre 0 e 100% no PWM.
--
--      Para permitir gerar sinais de frequencias mais baixas, um circuito
--  de "prescaler" é implementado com um outro contador de oito bits.    A
--  contagem é incrementada até se igualar a um valor de entrada de controle.
--  Quando aquela entrada é X"00", o fator de prescaler é unitário.
--
--
--  Por:  Prof. Édson Mélo, em março de 2013.
--        Instituto Federal Santa Catarina /DAMM
--        Florianópolis /SC
---------------------------------------------------------------------------
--
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity modulo_pwm is

  port (
    CLK, RSTn,
    HABILITA,
    INV_PWM,
    HAB_SAIDAn
    : in std_logic;
    TAXA      : in std_logic_vector (7 downto 0);
    PRESCALER : in std_logic_vector (7 downto 0);

    PWM : out std_logic
    );
end entity modulo_pwm;


architecture comportamento of modulo_pwm is
  signal PRESC_ENABLE : std_logic;
  signal PRE_PWM      : std_logic;

begin

  gera_prescaler : process (CLK, RSTn, PRESCALER, HABILITA)
    variable cont_prescaler : std_logic_vector (7 downto 0) := X"00";
    variable hab_prescaler  : std_logic                     := '0';
  begin

    if (HABILITA = '0') then
      hab_prescaler := '0';
    else
      if (RSTn = '0') then
        cont_prescaler := X"00";

      elsif rising_edge (CLK) and PRESCALER /= X"00" then
        cont_prescaler := cont_prescaler + 1;
        if (cont_prescaler = PRESCALER) then
          cont_prescaler := X"00";
        end if;
      end if;

      if (cont_prescaler = X"00") then
        hab_prescaler := '1';
      else
        hab_prescaler := '0';
      end if;
    end if;

    PRESC_ENABLE <= hab_prescaler;

  end process;

  gera_pwm : process (CLK, RSTn, PRESC_ENABLE)
    variable CONTAGEM  : std_logic_vector (7 downto 0) := X"00";
    variable SAIDA_PWM : std_logic                     := '0';

  begin

    if (RSTn = '0') then
      CONTAGEM  := X"00";
      SAIDA_PWM := '0';

    elsif (rising_edge (CLK) and PRESC_ENABLE = '1') then

      if ('0' & taxa = CONTAGEM) then
        SAIDA_PWM := '0';
      elsif (CONTAGEM = X"00") then
        SAIDA_PWM := '1';
      end if;

      CONTAGEM := CONTAGEM + 1;
      if CONTAGEM = X"FF" then
        CONTAGEM := X"00";
      end if;
    end if;

    PRE_PWM <= SAIDA_PWM;
  end process;

  ajusta_saida : PWM <=
    PRE_PWM     when INV_PWM = '0' and HAB_SAIDAn = '0' else
    not PRE_PWM when INV_PWM = '1' and HAB_SAIDAn = '0' else
    'Z';

end architecture comportamento;
